'''
    非存储入数据库模型区

        此处主要是存放一些不需要存放入数据库的模型
'''
import threading

from CoreSubSystem.models import user
from UserSubSystem.models import userToField


class UserChoiceSingleton(object):
    '''用户选择保存类

        描述
        ========
        因为用户选择是多变的,所以没有必要存储在数据库中
        此处使用了双重线程锁来保证线程安全的单例模式;
    '''
    _instanceLock = threading.Lock()

    # 创建一个字典用于临时存储用户的选择
    user_choice_dict = {}

    # 创建一个字典用于临时用户关于风险资产占比的选择
    user_risk_degree_dict = {}

    def __init__(self):
        pass

    def __new__(cls, *args, **kwargs):
        msg = "请使用get_instance方法!"
        raise NotImplementedError(msg)

    @classmethod
    def get_instance(cls):
        '''
        单例模式获取对象方法
        :return:该单例模式实例
        '''
        if not hasattr(cls, "_instance"):
            # 来获取对象时先加锁，防止创建多个对象
            with cls._instanceLock:
                if not hasattr(cls, "_instance"):
                    # 此时尚不存在，调用构造函数创建对象
                    cls._instance = super(UserChoiceSingleton, cls).__new__(cls)
        return cls._instance

    @classmethod
    def create_user_choice(self,user_choice):
        '''
        创建一个用户的选择,将其放置在字典中
        '''

        # 先加个锁，防止并发访问造成数据的不一致性
        with self._instanceLock:
            self.user_choice_dict[user_choice["user_id"]] = user_choice
            finded_user = user.objects.get(id=user_choice["user_id"])
            finded_user.user_annual_income = user_choice["user_annual_income"]
            finded_user.user_expected_return_rate = user_choice["user_expected_return"]
            finded_user.user_expected_time = user_choice["user_expected_time"]
            finded_user.save()
            # 清空原先数据
            all_this_user_fields = userToField.objects.filter(user_id=finded_user).all()
            all_this_user_fields.delete()

            fields = user_choice["fund_field"]
            for each_field_id in fields:
                user_to_field = userToField()
                user_to_field.user_id = finded_user
                user_to_field.fund_field_id_id = int(each_field_id)
                user_to_field.save()


    @classmethod
    def get_user_choice(self, user_id):
        '''
        根据用户的id获取用户的选择
        '''

        try:
            return self.user_choice_dict[user_id]
        except Exception as e:
            # 默认返回的都是按照最保守的算
            return {
                "user_annual_income":"1",
                "user_expected_time":"1",
                "user_expected_return":"0.003",
                "fund_field":[1]
            }

    @classmethod
    def create_user_risk_degree_choice(self, user_risk_degree_choice):
        '''
        创建一个用户的风险资产占比选择,将其放置在字典中
        '''

        # 先加个锁，防止并发访问造成数据的不一致性
        with self._instanceLock:
            finded_user = user.objects.get(id=user_risk_degree_choice["user_id"])
            finded_user.user_risk_ratio = user_risk_degree_choice["user_risk_ratio"]
            finded_user.save()
            self.user_risk_degree_dict[user_risk_degree_choice["user_id"]] = user_risk_degree_choice

    @classmethod
    def get_user_risk_degree_choice(self, user_id):
        '''
        根据用户的id获取用户对风险资产占比选择
        '''

        try:
            return self.user_risk_degree_dict[user_id]
        except Exception as e:
            # 默认返回用户的风险资产占比指数为0.1
            return {
                "user_risk_ratio":0.1
            }

class FundListSingleton(object):
    '''基金序列保存类

        描述
        ========
        此处放置着经过筛选过后的基金的列表,因为"4433"筛选法则比较耗时间,大概消耗1~2分钟,所以只能在系统运作之初就先筛选一波，之后通过定时任务框架进行下一步筛选
        此处使用了双重线程锁来保证线程安全的单例模式;

        注意:
            1. 此处的风险基金列表中的对象是此子系统下的 fundToFundField 类的实例对象
            2. 此处的债券型基金列表的对象是此子系统下的 fund 类的实例对象
            3. 差异的原因是:风险基金列表有基金领域,用户需要通过此种方法筛选;而债券型基金没有基金领域,不需要通过用户所选择领域进行筛选;
    '''
    _instanceLock = threading.Lock()



    # 经过"4433"筛选法则筛选出的一到两年的股票与混合型基金对应基金领域类列表
    selected_risk_fund_list_for_one_or_two_years = []

    # 经过"4433"筛选法则筛选出的三年的股票与混合型基金对应基金领域类列表
    selected_risk_fund_list_for_three_years = []

    # 经过"4433"筛选法则筛选出的四年的股票与混合型基金对应基金领域类列表
    selected_risk_fund_list_for_four_years = []


    '''
        此为风险类基金与债券类基金的分界线
    '''


    # 经过"4433"筛选法则筛选出的一到两年的债券型基金列表
    selected_bond_fund_list_for_one_or_two_years = []

    # 经过"4433"筛选法则筛选出的三年的债券型基金列表
    selected_bond_fund_list_for_three_years = []

    # 经过"4433"筛选法则筛选出的四年的债券型基金列表
    selected_bond_fund_list_for_four_years = []

    def __init__(self):
        pass

    def __new__(cls, *args, **kwargs):
        msg = "请使用get_instance方法!"
        raise NotImplementedError(msg)

    @classmethod
    def get_instance(cls):
        '''
        单例模式获取对象方法
        :return:该单例模式实例
        '''
        if not hasattr(cls, "_instance"):
            # 来获取对象时先加锁，防止创建多个对象
            with cls._instanceLock:
                if not hasattr(cls, "_instance"):
                    # 此时尚不存在，调用构造函数创建对象
                    cls._instance = super(FundListSingleton, cls).__new__(cls)
        return cls._instance

    @classmethod
    def get_risk_list(self,year='1'):
        '''

        :param year: 选择返回的基金列表是几年的
        :return: 经过"4433"基金法则筛选过的基金列表。
        '''

        if year == '1' or year == '2':
            return FundListSingleton.get_instance().selected_risk_fund_list_for_one_or_two_years
        elif year == '3':
            return FundListSingleton.get_instance().selected_risk_fund_list_for_three_years
        elif year == '4':
            return FundListSingleton.get_instance().selected_risk_fund_list_for_four_years
        else:
            return []

    @classmethod
    def get_bond_list(self, year='1'):
        '''

        :param year: 选择返回的基金列表是几年的
        :return: 经过"4433"基金法则筛选过的基金列表。
        '''

        if year == '1' or year == '2':
            return FundListSingleton.get_instance().selected_bond_fund_list_for_one_or_two_years
        elif year == '3':
            return FundListSingleton.get_instance().selected_bond_fund_list_for_three_years
        elif year == '4':
            return FundListSingleton.get_instance().selected_bond_fund_list_for_four_years
        else:
            return []

